﻿using System.Diagnostics;
using DimensionsHelper.LanguageService.Syntax;

namespace DimensionsHelper.LanguageService;

public partial class Preprocessor
{
    /// <summary>
    ///     读取 #if / #elif 后的 defined[(]macr_name[)] 表达式，macro_name前后
    ///     可以有括号或没有括号。
    /// </summary>
    /// <param name="definedKeywordToken">defined 关键字</param>
    private DefinedExpression ProcessDefinedExpression(SourceNode definedKeywordToken)
    {
        // defined
        int start = definedKeywordToken.Start;
        // 允许忽略括号
        SourceNode? leftParen = null;
        SourceNode? rightParen = null;
        if (TryProcess(SyntaxKinds.LeftParenthesisToken, out SourceNode? left))
        {
            leftParen = left;
        }

        // 宏名称
        SourceIdentifier macroName = SourceIdentifier.DefaultIdentifier;
        if (NextExpect(SyntaxKinds.Identifier, false, out SourceNode? nameNode))
        {
            macroName = NodeFactory.CreateSourceIdentifier(nameNode);
        }

        if (leftParen != null)
        {
            NextExpect(SyntaxKinds.RightParenthesisToken, false, out rightParen);
        }

        DefinedExpression expr = NodeFactory.CreateDefinedExpression(null, definedKeywordToken, macroName);
        expr.Update(macroName, leftParen, rightParen);
        FinishToken(expr, start);
        return expr;
    }


    /// <summary>
    ///     读取 #if / #elif 后的一元表达式
    /// </summary>
    /// <param name="operatorToken">一元表达式操作符</param>
    private PreUnaryExpression ProcessUnaryExpression(SourceNode operatorToken)
    {
        PreExpression expr = ProcessExpression(false);
        PreUnaryExpression unaryExpr = NodeFactory.CreatePreUnaryExpression(null, operatorToken, expr);
        return FinishToken(unaryExpr, operatorToken.Start, expr.End - operatorToken.Start);
    }


    /// <summary>
    ///     读取参数宏调用表达式，<paramref name="callee" />需要是标识符。
    /// </summary>
    /// <param name="callee">调用节点，需要是<see cref="SyntaxKinds.Identifier" /></param>
    /// <param name="leftParenthesisToken"><paramref name="callee" />后的左括号</param>
    /// <returns>读取后的参数宏调用表达式</returns>
    private MacroCallExpression ProcessMacroCallExpression(SourceNode callee, SourceNode leftParenthesisToken)
    {
        Debug.Assert(callee.IsIdentifier);
        Debug.Assert(leftParenthesisToken.IsNode(SyntaxKinds.LeftParenthesisToken));

        MacroCallExpression callExpr = NodeFactory.CreateMacroCallExpression(null, callee, null);
        callExpr.Update(callee.Start);
        // )
        SourceNode? rightParenthesisToken = null;

        // -> )
        List<MacroArgument> argList = new();
        // 宏调用允许换行
        while (!_lexer.Eof() && !_currentNode.IsNode(SyntaxKinds.RightParenthesisToken))
        {
            SourceNode tokenNode = ProcessNode();
            if (tokenNode.IsNode(SyntaxKinds.RightParenthesisToken))
            {
                rightParenthesisToken = tokenNode;
                break;
            }

            argList.Add(ProcessMacroArgument(tokenNode));
        }

        MacroArgumentList arguments = NodeFactory.CreateMacroArgumentList(callee,
            leftParenthesisToken, rightParenthesisToken, argList);
        // 参数列表从'('开始，到')'结束
        arguments.Update(_lexer.GetText(leftParenthesisToken.Start,
            _currentNode.End - leftParenthesisToken.Start));
        // 更新参数列表和文本
        callExpr.Update(arguments).Update(callee.Start,
            _currentNode.Width, _lexer.GetText(callee.Start, _currentNode.End - callee.Start));
        return callExpr;
    }


    /// <summary>
    ///     创建指定起始位置的字符串节点，对应文本将从原始文本中截取。
    /// </summary>
    /// <param name="parent">父节点</param>
    /// <param name="start">起始位置</param>
    /// <param name="length">文本宽度</param>
    private SourceString ProcessString(SourceNode? parent, int start, int length)
    {
        SourceString str = NodeFactory.CreateSourceString(parent);
        FinishToken(str, start, length);
        return str;
    }


    /// <summary>
    ///     处理参数宏调用表达式的参数节点，参数可以接收除了','和')'的任意类型节点。
    /// </summary>
    /// <returns>读取到的参数宏参数</returns>
    private MacroArgument ProcessMacroArgument(SourceNode node)
    {
        int start = _currentNode.Start;
        int length = 0;
        MacroArgument arg = NodeFactory.CreateMacroArgument(null);

        while (!_lexer.Eof())
        {
            if (node.IsNode(SyntaxKinds.CommaToken) ||
                node.IsNode(SyntaxKinds.RightParenthesisToken) ||
                node.HasPrecedingLinebreak)
            {
                if (node.IsNode(SyntaxKinds.CommaToken))
                {
                    arg.Update(node);
                }

                break;
            }

            arg.AddNode(node);
            length = node.End - start;
            node = ProcessNode();
        }

        return FinishToken(arg, start, length);
    }


    /// <summary>
    ///     处理左侧表达式
    /// </summary>
    /// <param name="token">表达式起始的节点</param>
    private PreExpression ProcessExpressionOperand(SourceNode token)
    {
        // not | ! | -
        if (token.IsPreUnaryOperator)
        {
            return ProcessUnaryExpression(token);
        }

        // defined [(] Macro_Name [)]
        if (token.IsNode(SyntaxKinds.PreDefinedKeywordToken))
        {
            return ProcessDefinedExpression(token);
        }

        switch (token.Kind)
        {
            case SyntaxKinds.String:
                return NodeFactory.CreateSourceString(token);

            case SyntaxKinds.Integer:
                return NodeFactory.CreateSourceInteger(token);

            case SyntaxKinds.Double:
                return NodeFactory.CreateSourceDouble(token);

            case SyntaxKinds.TrueKeywordToken:
            case SyntaxKinds.FalseKeywordToken:
                return NodeFactory.CreateSourceBoolean(token);

            case SyntaxKinds.NullKeywordToken:
                return NodeFactory.CreateSourceNull(token);

            case SyntaxKinds.Identifier:
                // -> (
                if (TryProcess(SyntaxKinds.LeftParenthesisToken, out SourceNode? leftParenthesisToken))
                {
                    return ProcessMacroCallExpression(token, leftParenthesisToken);
                }

                return NodeFactory.CreateSourceIdentifier(token);

            default:
                Raise(DiagnosticDescriptors.InvalidToken, token, token.Text);
                return NodeFactory.CreatePreEmptyExpression(null, _currentNode.Start);
        }
    }


    /// <summary>
    ///     处理二元表达式，优先级更高或者两侧带有括号的表达式位于树的下层。
    /// </summary>
    /// <param name="leftOperand">左侧表达式</param>
    /// <param name="operatorToken">二元操作符节点</param>
    /// <returns>二元表达式</returns>
    /// <remarks>
    ///     对于表达式 A * B + C / (D - E)
    ///     结果应当如下：
    ///     <code>
    ///        +
    ///     /     \
    ///    *      /
    ///   / \   /   \
    ///  A   B C     -
    ///             / \
    ///            D   E
    /// </code>
    /// </remarks>
    private PreBinaryExpression ProcessBinaryExpression(PreExpression leftOperand, SourceNode operatorToken)
    {
        PreExpression right = !IsNextWrap()
            ? ProcessExpression()
            : // 优先级高的操作符在AST的底层，此处从右向左结合
            NodeFactory.CreatePreEmptyExpression(null, _currentNode.End);

        if (!right.IsEmpty && leftOperand.IsNode(SyntaxKinds.BinaryExpression))
        {
            PreBinaryExpression leftBinary = (PreBinaryExpression)leftOperand;
            if (right.LeftParenthesisToken == null && /* 带有括号的表达式不进行优先级检查 */
                SyntaxFacts.TryConvertSyntaxKindsToOperatorPriority(leftBinary.OperatorToken.Kind,
                    OperatorContexts.Preprocessor, out int leftPriority) &&
                SyntaxFacts.TryConvertSyntaxKindsToOperatorPriority(operatorToken.Kind,
                    OperatorContexts.Preprocessor, out int thisPriority) &&
                thisPriority > leftPriority)
            {
                //  A + B * C
                //     +            +
                //    / \   --->   / \
                //   A   B        A  *
                //                  / \
                //                 B  C
                PreExpression leftRight = leftBinary.RightOperand;
                PreBinaryExpression rightBinary = NodeFactory.CreatePreBinaryExpression(null,
                    leftOperand, operatorToken);
                rightBinary.Update(leftRight.Start, right.Width,
                    _lexer.GetText(leftRight.Start, right.End - leftRight.Start));
                leftBinary.Update(rightBinary);
                leftBinary.Update(right.Width,
                    _lexer.GetText(leftBinary.Start, right.End - leftBinary.Start));
                return leftBinary;
            }
        }

        PreBinaryExpression expr = NodeFactory.CreatePreBinaryExpression(null, leftOperand,
            operatorToken, right);
        FinishToken(expr, leftOperand.Start);
        return expr;
    }


    /// <summary>
    ///     扫描预处理器表达式，出现在 #if / #elif 后。
    /// </summary>
    /// <param name="startNode">起始位置</param>
    /// <param name="processBinaryOperator">
    ///     是否处理后续的二元操作符，只有在<see cref="ProcessUnaryExpression(SourceNode)" />
    ///     中，此值才是<see langword="false" />
    /// </param>
    /// <returns>扫描后的预处理表达式</returns>
    private PreExpression ProcessExpression(SourceNode startNode, bool processBinaryOperator = true)
    {
        if (startNode.IsNode(SyntaxKinds.LeftParenthesisToken))
        {
            SourceNode? rightParen = null;
            SourceNode leftParen = startNode;
            PreExpression innerExpr = ProcessExpression();
            if (Expect(SyntaxKinds.RightParenthesisToken))
            {
                rightParen = _currentNode;
            }

            FinishToken(innerExpr, startNode.Start);
            return innerExpr.Update(leftParen, rightParen);
        }

        // ) -> 空表达式
        if (startNode.IsNode(SyntaxKinds.RightParenthesisToken))
        {
            PreEmptyExpression emptyExpr = NodeFactory.CreatePreEmptyExpression(startNode.Start);
            return FinishToken(emptyExpr, "");
        }

        PreExpression expr = ProcessExpressionOperand(startNode);

        // 二元表达式
        if (IsNextWrap() || !processBinaryOperator)
        {
            return expr;
        }

        if (!TryProcess(node => node.IsPreBinaryOperator, out SourceNode? token))
        {
            return expr;
        }

        expr = ProcessBinaryExpression(expr, token);
        expr.Update(_currentNode.Width,
            _lexer.GetText(startNode.Start, _currentNode.End - startNode.Start));
        return expr;
    }


    /// <summary>
    ///     以下一个节点为起始节点扫描预处理表达式。
    /// </summary>
    /// <param name="processBinaryOperator">
    ///     是否处理后续的二元操作符，只有在<see cref="ProcessUnaryExpression(SourceNode)" />
    ///     中，此值才是<see langword="false" />
    /// </param>
    private PreExpression ProcessExpression(bool processBinaryOperator = true)
    {
        return ProcessExpression(ProcessNode(), processBinaryOperator);
    }
}